﻿/**
 * jQEditor - WYSIWYG HTML Editor for Internet
 * Copyright (c) 2011, jQEditor.com All rights reserved.
 * MIT Licensed.
 * Depends: jQuery1.32+
 * Date: 2011-09-05
 * Version: 1.0.1
 *
 * 编辑器核心
 * 编辑器框架，不包含具体功能，所有功能通过模块或插件形式进行扩展
*/

(function( $ ){
var timestamp = +new Date(), logs={}, debug = false, logCount=0;
var datestamp = (function(){
	var d = new Date(),
		m = d.getMonth()+1,
		Y = d.getFullYear( );
	m = m<10 ? '0' + m : m;
   return ''+ Y + m + d.getDate();
})();

debug = ( typeof JQEDEBUG !== "undefined" && JQEDEBUG)? true:false;

/**
 * 对外公开的编辑对象 jQEditor，相当于局部变量 E 的对外委托代理人。
 * js对象永远都是引用传值，如果想避免引用传值只能克隆。
 */

/**
 * 编辑器最终使用的方法
 */
var jQEditor = window.jQEditor = $.editor = function( option ){

	return new _Editor( option );
}
/*
function getJqePath(){
	var path = '';
	var jqeScript = $("#jqeditor");
	if( jqeScript.size() ){
		path = jqeScript.attr('jqepath');
	}
	return path || './';
}

jqePath = getJqePath();
// 加载编辑器通用css
$('head').append('<link id="jqeditor-base-css" rel="stylesheet" type="text/css" href="'+jqePath+'/skins/skin_base.css" />');
*/

function _Editor( option ){
	this.option = option;

	E.create(option);
}

_Editor.prototype = {
	// 获取编辑器内容
	getHtml: function(){

	},
	// 设置编辑器内容
	setHtml: function(html){

	},
	// 编辑器内容是否发生了改变
	ischange: function(){

	},

	// 向光标处插入html
	pasteHTML: function(html){

	},

	// 锁定编辑器
	lock: function(){

	},

	// 解锁编辑器
	unlock: function(){

	},

	// 显示提示信息
	notice: function(msg){

	},

	// 显示错误信息
	error: function(msg){

	}
}

//定义一些常用的变量，
//代码当中尽量不要直接使用类似这样的"undefined"字符串
var Consts = {
	typeof_function :"function",
	re_string		:/string/i,
	re_object		:/object/i,
	re_obj_fn		:/object|function/i,
	IE : $.browser.msie,
	mozilla: $.browser.mozilla,
	script_path: '',
	returnFalse: function( ){
		return false;
	},

	baseCmds: [
		'Cut','Copy','Paste','Bold','Italic','Underline','StrikeThrough',
		'InsertOrderedList','InsertUnorderedList',
		'JustifyLeft','JustifyCenter','JustifyRight','JustifyFull',
		'Outdent','Indent','SubScript','SuperScript','RemoveFormat'
	],

	//这些命令 closeStyleWithCSS
	closeStyleCmd: /bold|italic|underline|strikeThrough/i,

	IE6: ( $.browser.msie && $.browser.version == '6.0' ),
	IE678: ( $.browser.msie && /[678]/.test($.browser.version) )
};

/**
 * 记录日志
 *
 * @param type string:
 *  run    : 运行
 *  event  : 触发编辑器事件
 *  error  : 错误
 *  warning：警告
 *  notice ：提示
 *
 */
function log(type, msg){
	if(!debug){return;}
	if(!type){
		return logs;
	}

	if( typeof type !== 'string' ){
		alert("log(type, msg)方法的参数type必须是有效的字符串");
		return false;
	}
	type = $.trim(type);

	if( msg && typeof msg === 'string' ){
	//添加日志信息
		if( !logs[type] ){
		//如果此type不存在则初始化一个数组
			logs[type] = [];
		}

		//添加成功
		logs[type].push(msg);

		if(typeof console === 'object'){
			logCount += 1;
			msg = logCount + "." + msg;
			switch(type){
				case 'run': console.log(msg);break;
				case 'warning': console.warn(msg);break;
				case 'error': console.error(msg);break;
				default : console.log(msg);
			}
		}
		return true;
	} else if( logs[type] ){
	//获取指定类型的日志信息
		return logs[type];
	}else{
	//获取所有日志
		return logs;
	}
}
log('run', 'start');

function ln(key){
	jQEditor[key] = function(name, obj, value){

		//公共代码

		var data = E[key](name, obj, value);

		//公共代码

		return data;
	}
};

$.extend(jQEditor, {
	consts: Consts,
	eid: '',

	//编辑器内容的样式
	styleContent: [],

	//编辑器的样式
	styleSkin:[],

	//这三个方法主体相同，改为通过 this.add() 统一扩展
	//类似这样的改进，不要直接修改代码
	//将被改进的代码注释，并说明原因，方便他人后续维护。
	// extend: function(name, obj){
		// E.extend(name, obj);
	// },
	// plugin: function(name, obj){
		// E.plugin(name, obj);
	// },
	// mod: function(name, obj){
		// E.mod(name, obj);
	// }

	init: function(){
		/*
		this.add('extend').add('mod').add('plugin').add('icon')
			.add('lang').add('log')
			.add('action').add('pasteHTML').add('execCommand');
		*/

		var i=0, len=0, key='',
			cmds = ['extend', 'mod', 'plugin', 'icon', 'lang', 'log',
				'showError','showNotice','alert', 'hideMessage', 'addFilter',
				'getHTML', 'setHTML', 'hidePanel','$','pluginExit',
				'action', 'pasteHTML', 'execCommand', 'iframe', 'getButtonByName'
			];

		log('run', 'init: '+cmds.join(',') );

		for(i=0, len = cmds.length; i<len; i++){
			key = cmds[i];

			/*
			// 2011-04-17 潘雪鹏
			// 这样有问题
			this[key] = function(name, obj, value){
				// ...
			}
			*/

			ln( key );
		}
	},

	/**
	 * 将 E.extend 等方法添加到 jQEditor 对象当中，
	 * 可以在这里添加一些公共代码。
	 * @param key string
	 * 		方法名
	 */
	/*
	// 2011-03-04 17:40 潘雪鹏
	// 这里提供add方法不安全，会导致完全对外暴露内部对象E
	add: function(key){
		this[key] = function(name, obj, value){

			//公共代码

			var data = E[key](name, obj, value);

			//公共代码

			return data;
		}
		return this;
	},
	*/

	config: function(key, value){
		if( value && key ){
			E.config[key] = value;
			return this;
		}else if( key && typeof key === 'object' ){
			E.config = $.extend(E.config, key);
			return this;
		}else if( key && typeof key === 'string' ){
			return E.config[key];
		}else{
			return E.config;
		}
	},

	get: function(name){
		if( /(option|body|document|window|iframe)/.test(name) ){
			return E.get(name);
		}
	},

	setEid: function( eid ){
		E.setEid( eid );
	},

	focus: function( dir ){
		var self = this;
		if( typeof dir !== "undefined" ){
			var o, el, Selection = E.mod("Selection"), body=E.get('document').body;
			if( dir == 'first' ){
				el = body.firstChild;
				dir = true;
			}else{
				el = body.lastChild;
				dir = false;
			}
			Selection.selectNode(el, dir);
			self.scrollToElement( dir );
		}else{
			E.focus();
		}
	},

	// 滚动页面到编辑器的底部或顶部
	// dir: 顶部 true, 底部 false
	scrollToElement: function ( dir ){
		var body = (window.opera)? (document.compatMode=="CSS1Compat"? $('html') : $('body')) : $('html,body'),
			box = $("#"+E.eid),
			min = $(document).scrollTop(),
			max = min + $(window).height() - box.height(),
			scrollTop = dir ? box.offset().top: box.offset().top + box.height();

		// 不在可视范围，则自动滚动
		if( scrollTop < min || scrollTop > max ){
			scrollTop -= 200;
			if(scrollTop < 0) scrollTop = 0;
			body.animate({scrollTop: scrollTop}, 200);
		}

		var dom = E.get('document');
		// 滚动编辑器区域到顶部或底部
		$(dom).scrollTop( dir ? 0: 9999999 );
	},

	monitor: function(cmd){
		E.monitor( cmd );
	},

	// 禁用插件
	disablePlugin : function ( name ){
		Plugin = E.plugin( name );
		Plugin && Plugin.disable();
	},

	// 启用插件
	enablePlugin : function ( name ){
		Plugin = E.plugin( name );
		Plugin && Plugin.enable();
	},

	// 隐藏插件
	hidePlugin : function ( name ){
		Plugin = E.plugin( name );
		if(Plugin){
			Plugin.hide();
			return Plugin;
		}
	},

	//执行插件
	clickPlugin : function ( name ){
		var Plugin = E.plugin(name);
		if(Plugin){
			Plugin.click( E.mod('Panel'), Plugin );
			return Plugin;
		}
	},

	// 获取准备提交的内容
	getSubmitHtml: function( eid ){
		if( eid ){
			E.setEid(eid);
		}
		var plugin = E.plugin('Cleanup');
		plugin && plugin.click();

		return E.getHTML( true );
	}
});

jQEditor.init();

/**
 * E是编辑器对象，是局部变量，外部不能访问，
 * E的方法通过其自身的extent方法进行扩展，
 * 通过 jQEditor 对象对外仅公开少数的几个方法。
 */
var E = {
	version: '1.0',
	config: {},
	bodys: {},
	iframes: {},
	documents: {},
	windows: {},
	options: {},
	eid: '',

	//插件当中定义的风格样式
	pluginStyle:[],

	//所有自定义插件对象，键值是插件名
	plugins:{},

	//所有核心和插件之外的功能，如 panel, toolbar, menu, selection, event, data 等等
	mods:{},

	icons:{},

	//编辑器选项的默认配置
	defaults:{},

	//扩展 $.editor 核心，默认仅可实现一次，被实现后都会有记录
	extend:function(name, obj){},

	//记录被实现过的属性名称，已经存在的方法不能被覆盖
	isExtend:[],

	//(添加|获取)插件
	plugin:function(name, obj){},

	//(添加|获取)模块
	mod:function(name, obj){},

	icon:function(name, obj){},

	langs:{},

	//创建一个编辑区域
	open:function(option){},

	//记录运行时的一些问题
	log: function(type, msg){},

	// 数据过滤器，每个过滤器都是一个对象{check: function(){}, replace: function(){}}
	domFilters: {},
	htmlFilters: {},
	htmlOutFilters: {}
};

/**
 * 扩展编辑器自身，用来扩展编辑器对象 E 的核心方法，
 * 可以避免E对象过长，也利于代码的书写。
 * @param string name
 * @param object obj
*/
E.extend = function( name, obj ){
	if(!name || typeof name !== 'string'){
		log('error', "E.extend(name, obj)方法的参数name必须是有效的字符串");
		return this;
	}
	name = $.trim(name);

	if ( E.isExtend[name] ){
		log('run', '编辑器已经存在名字为'+ name +'对象');
	}else{
		E[name] = obj;
		E.isExtend[name] = 1;
	}

	return this;
}



/**
 * 监测编辑区域内容
 *
*/
var isInMonitor = false;
E.extend("monitor", function E_monitor( cmd ){
	if( !isInMonitor ){
		cmd = cmd || '';
		E.log('run', 'E.Event.trigger( {type:onmonitor, cmd:'+cmd+'} );');
		E.Event.trigger( {type:'onmonitor', cmd:cmd} );
		isInMonitor = true;
		setTimeout(function(){
			isInMonitor = false;
		}, 300);
	}
});

/**
 * 获取E的属性
 * @name string 属性名称
 * 比如 E.get("option") 返回 E.options[ E.eid ]，所以 name += "s"
*/
E.extend("get", function E_get( name, eid ){
	name += 's';
	eid = eid || E.eid;
	if(E[name] && E[name][eid]){
		return E[name][eid];
	}else{
		if( !E[name] ){
			log('warning', '编辑器不存在变量:'+name);
		}else{
			log('warning', '编辑器eid='+eid+'不存在');
		}
		return ;
	}
});

/**
 * 设置 E.eid
 * @name string 属性名称
 * @name string 属性值
*/
E.extend("setEid", function E_setEid( eid ){
	if( eid && typeof eid === 'string' && E.options[eid]){
		// 只有是有效的eid才可以设置成功
		if( eid !== E.eid){
			E.eid = jQEditor.eid = eid;

			// 把插件相关的配置信息覆盖到插件
			// ...
			// plugin: {
			//		pluginName: {
			//			title: '源码',
			//			config: {
			//				name: value,
			//				...
			//			}
			//		},
			//		...
			// }
			var pluginOptions = E.options[eid].plugin,
				option = null,
				Plugin = null;
			if( pluginOptions && typeof pluginOptions === 'object' ){
				for( var pluginName in pluginOptions ){
					option = pluginOptions[pluginName];
					Plugin = E.plugin(pluginName);
					if( option && typeof option === 'object' ){
						for( var key in option ){
							if( Plugin[key] ){
								var value = option[key];
								if( /^(string|number|boolean)$/i.test(typeof value) ){
									Plugin[key] = value;
								}else if( typeof value === 'object' ){
									for( var k in value ){
										Plugin[key][k] = value[k];
									}
								}
							}else{
								log('warning', '插件'+pluginName+'没有key值：'+key);
							}
						}

						if( typeof Plugin.init === 'function' ){
							Plugin.init(Plugin);
						}
					}
				}
			}
			log('run', 'setEid='+eid);
		}
	}else{
		log('error', '请检查eid='+eid+'是否正确');
	}
});

/**
 * 弹出框警告
 * @msg string 信息字符串
*/
E.extend("alert", function E_alert( msg ){
	alert( msg );
});

// 显示错误信息
E.extend("showError", function E_errorMsg( msg, duration ){
	var Message = E.mod("Message");

	if(Message){
		Message.showError(msg, duration);
	}
});

// 显示编辑器提示信息
// 提示信息一般在工具栏下面滑动显示
E.extend("showNotice", function E_showNotice( msg, duration ){
	var Message = E.mod("Message");

	if(Message){
		Message.showNotice(msg, duration);
	}
});

// 隐藏编辑器提示信息
E.extend("hideMessage", function E_showNotice( speed ){
	var Message = E.mod("Message");

	if(Message){
		Message.hide( speed );
	}
});

// 编辑器$
E.extend("$", function E_$( s ){
	var dom = E.get("document");
	return $(s, dom);
});

/**
 * 记录启动运行的日志信息，以方便调式
 * @param string type
 *		消息的类型，随意指定
 * @param string msg
 * @return
*/
E.extend("log", function E_log(type, msg){
	return log(type, msg);
});

// 模块基础对象，所有添加的模块都会继承此对象的方法
function BaseModel( ){
	this.enabled = 1;
	this.logs = [];
}
BaseModel.prototype = {
	isEnabled: function(){
		return this.enabled;
	},
	enable: function( ){
		this.enabled = 1;
	},

	disable: function( msg ){
		this.enabled = 0;
		if(msg){this.log(msg);}
	},

	log: function( msg ){
		this.logs.push( msg );
	}
}

/**
 * 实现 mod 方法，$.mod(name, obj)增加一个模块，$.mod(name)获取一个模块
 * @param string name
 * @param object obj
 * @return boolean|obj
 */
E.extend("mod", function E_mod( name, obj ){
	if(!name || typeof name != "string"){
		log('error', "E.mod(name, obj)方法的参数 name 必须是有效的字符串");
		return 0;
	}
	var mod, mods = E.mods, basemod;
	if( obj && typeof obj === 'object' ){
	//添加模块
		if( mods[name] ){
		//已经存在，注册模块失败
			log('error', '模块'+name+'已经存在，注册失败');
			return 0;
		} else {
		//注册模块
			//将模块 name 属性设置为参数 name
			log('run', '模块'+name+'注册成功');

			obj['name'] = name;
			mods[name] = $.extend(new BaseModel(), obj);
			return 1;
		}
	} else if( mods[name] ){
	//获取模块
		mod = mods[name];
		return mod.isEnabled() ? mod : 0;
	}else{
		log('error', '模块'+name+'不存在');
	}
});

/**
 * 插件的基础对象，所有自定义插件均继承及对象，
 * 方便给所有插件统一扩展属性，
 * 基础命令（Consts.baseCmds）插件不需要继承此对象。
 *
 * 提示：2011.01.28 潘雪鹏
 * BasePlugin 由字面量对象形式改为了类形式，
 * 之前 var BasePlugin = {...};
 * 现在 function BasePlugin(){this.*=*; ...}
 * 原因是多个插件公用一个对象时会出现问题，
 * 现象是所有插件的界面都显示最后一个插件的界面，
 * 而使用类形式后会给每个插件 new 一个实例对象，从而不会导致冲突。
 *
 * 如果想重现此问题可以参考core.js的第15 svn版本。
 */
function BasePlugin( ){
	//插件是否可用，当缺失依赖模块时会将此值置为 false
	this.enabled = 1;
	this.locked = 0;
	this.isNeedPanel = 1;
	this.isNeedTitle=1;
	this.title = '';
	this.config = null;
	this.style = '';
	this.menuTitle = '';
	this.menuType = 'Panel';//Panel|Menu

	//用来决定在FF webkit内核浏览器下使用那种方式插入html，base|custom
	this.inserthtml = 'base';

	//插件对应的面板对象，在 Panel.set 方法内会给 plugin.panel, plugin.Panel 赋值
	this.panel = null;
	this.event = null;

	// 插件面板需要屏蔽的事件，如killEvents = ['mousedown','click']
	this.killEvents = null;
	//缓存
	this.cache =[];

	this.logs = [];

	//选择对象
	this.Selection = null;

	//如果Plugin.one是一个方法，会在面板第一次打开时调用
	this.one = null;

	//如果Plugin.callback是一个方法，会在面板每次打开时调用
	this.callback = null;

	//this.click = function(){}
	//....
	//改为 this.prototype = {}
}

/**
 * 2011.01.28 23:22 潘雪鹏
 * 将 BasePlugin 类的方法由直接定义，改为使用 prototype 原型扩展。
 * 为什么这样修改？
 * 如此，BasePlugin 的所有实例化对象会共享这些方法，从而节省系统资源。
 */
BasePlugin.prototype = {

	// 2011-11-04 潘雪鹏
	// 基础插件不要包括init方法，init方法是用来判断是否需要面板的条件
	// 在plugin方法内，如果插件存在init方法，则会将isNeedPanel设置为1，否则设置为0
	//init: function(){},

	/**
	 * 由 E.action() 方法调用，本click方法再调用Panel.set( self )
	 * @param self object 插件自身
	 * @param Panel 面板对象E.mod('Panel')
	 */
	click: function( Panel, self, target ){
		if(Panel && self.isNeedPanel ){
			//设置面板
			Panel.set( self );
		}

		//调用插件的set方法
		if( typeof self.set === Consts.typeof_function ){
			self.set( Panel, self );
		}
		return true;
	},

	/**
	 * 点击面板上非【确定 取消】按钮区域时执行
	 * @param target jQuery 事件来源对象
	 * @param self object 插件自身
	 * @param panel  jQuery 面板实例对象
	 */
	action: function( target, panel, self ){

	},

	getCursorElement: function( ){
		var Selection = E.mod("Selection");
		return Selection.getElement();
	},

	getSelectionHtml: function( ){
		var Selection = E.mod("Selection");
		return Selection.getHtml();
	},

	getSelectionText: function( ){
		var Selection = E.mod("Selection");
		return ''+Selection.getText();
	},

	getTitle: function(){
		var option = E.get('option'),
			Lang = E.lang(option.lang),
			title = Lang[this.name];

		if( typeof title == 'string' ){
			return title;
		}else if( title && typeof title == 'object' && title.title ){
			return title.title;
		}else{
			return '未定义';
		}
	},

	/**
	 * 在点击插件的确定按钮后会执行
	 * 不需要在 plugin.ok 当中调用，Panel模块会自动调用 plugin.pasteHTML()
	 */
	pasteHTML: function( html ){
		html && E.pasteHTML( html );
	},

	/**
	 * 面板模块会调用此方法获取插件的html代码
	 */
	panelHtml: function( ){
		return this.cache['html'];
	},

	/**
	 * 得到当前面板当中所有表单项的值
	 */
	getValues: function( panel ){
		panel = panel || this.panel;
		var values={}, inputs = panel.find(":input").serializeArray();
		//input = [{name:"", value:""}, {}, ...]
		$.each(inputs, function(i, input){
			if(input.name){
				values[ input.name ] = input.value;
			}
		});
		return values;
	},

	error: function( msg ){
		var self = this, Panel=E.mod('Panel');
		if(Panel){
			Panel.error(msg);
		}
		self.unlock();
	},

	notice: function( msg ){
		var self = this, Panel=E.mod('Panel');
		if(Panel){
			Panel.notice(msg);
		}
	},

	//隐藏面板
	hide: function( ){
		var self = this, Panel=E.mod('Panel');
		if(Panel){
			Panel.hide( );
		}
	},

	isEnabled: function(){
		return this.enabled;
	},

	isDisabled: function(){
		return !this.enabled;
	},

	enable: function( ){
		if( this.enabled ){
			return;
		}
		this.enabled = true;

		var button = E.getButtonByName( this.name ),
			child = button.children(),
			title = child.attr('_title');
		button.removeClass("disabled");
		child.attr('title', title);
	},

	disable: function( msg ){
		if( !this.enabled ){
			return;
		}
		this.enabled = false;

		var cmd = this.name,
			button = E.getButtonByName( cmd ),
			child = button.children(),
			title = child.attr('_title');
		button.addClass("disabled");
		if ($.inArray(title,['复制(Ctrl + C)','剪切(Ctrl + X)','粘贴(Ctrl + V)']) > -1){
			title = title.replace (/\(/,'当前不可用,请使用(');
		} else {
			title + " 当前不可用";
		}
		child.attr('title', title);

		if(msg){this.log(msg);}
	},

	// 锁定的面板不能执行任何操作，也不能被关闭
	lock: function( lockTarget ){
		var Panel = E.mod('Panel'),
			Toolbar = E.mod('Toolbar');
		if( this.locked ){
			return;
		}
		this.locked = 1;

		if( lockTarget === 'toolbar' ){
			Toolbar.lock();
		}else if( lockTarget === 'panel' ){
			if( this.panel ){
				Panel.lock( this.panel );
			}
		}else{
			if( this.panel ){
				Panel.lock( this.panel );
			}

			Toolbar.lock();
		}
	},

	unlock: function( lockTarget ){
		var Panel = E.mod('Panel'),
			Toolbar = E.mod('Toolbar');
		if( !this.locked ){
			return;
		}
		this.locked = 0;

		if( lockTarget === 'toolbar' ){
			Toolbar.unlock();
		}else if( lockTarget === 'panel' ){
			if( this.panel ){
				Panel.unlock( this.panel );
			}
		}else{
			if( this.panel ){
				Panel.unlock( this.panel );
			}

			Toolbar.unlock();
		}
	},

	islock: function(){
		return this.locked;
	},

	log: function( msg ){
		this.logs.push( msg );
	}
}

/**
 * 加载插件或获取插件，插件不可以重复添加。
 * 可以一次添加多个插件，所有插件都会继承 Base_Plugin 对象的所有属性和方法。
 * @param string plugin
 * 		插件的名字，一般第一个字母大写，如Font
 * @param object obj
 *		插件对象
 * @return boolean|obj
 *
 * 2011-02-12 10:35
 * 基础插件（）改为使用 E.base_plugin 进行扩展，不再使用 E.plugin
*/
E.extend("plugin", function E_plugin( name, obj ){
	var typeof_obj = typeof obj, typeof_name = typeof name, plugins = E.plugins;
	//如果不判断name是否是string类型，可能会导致运行时错误
	//如果!name 或者 name既不是有效字符串也不是数组，则使用alert报错
	if(!name || (typeof_name != "string" && !$.isArray(name)) ){
		alert("E.plugin(name, obj)方法的参数 name="+ typeof_name +" 必须是有效的字符串或数组");
		return 0;
	}

	if( obj && Consts.re_obj_fn.test(typeof_obj) ){
	//添加插件
		var cmd, names, baseplugin, Plugin;
		if( typeof_name == "string" ){
			names = name.split(',');
		}else if( $.isArray(name) ){
			names = name;
		}

		for(var i=names.length-1; i>=0; i--){
			cmd = $.trim(names[i]);

			//兼容name当中可能存在 ,, 的情况
			if (!cmd){continue;}

			//基础插件可以进行覆盖添加
			//扩展插件不能覆盖添加，如果扩展插件已经存在则alert警告
			if ( plugins[cmd] ){
				if(plugins[cmd].type === 'base'){
					// 使用新插件覆盖已有插件
					plugins[cmd] = $.extend(plugins[cmd], obj);
					log('notice', '基础插件'+cmd+'被覆盖添加');
				}else{
					E.alert('提示：插件 '+ cmd +' 已经存在，不能重复添加！');
				}
				continue;
			}
			//这样不安全，需要使用 typeof 判断是否是对象
			//plugins[cmd] = obj;

			obj.name = cmd;
			if(typeof_obj === 'object'){
				baseplugin = new BasePlugin( );

				//所有插件均继承于 baseplugin
				plugins[cmd] = $.extend(baseplugin, obj);

				//2011.01.25 潘雪鹏
				//疑问：plugins[cmd]的方法，是否可以访问 obj 所在区域定义的私有变量？
				//回答：可以，因为obj里面的方法，还是以引用传值的方式赋值给了plugins[cmd]，
				//plugins[cmd]的方法也同样可以访问obj所在区域的私有变量。
			}else if(typeof_obj === 'function'){
				plugins[cmd] = obj;
			}

			Plugin = plugins[cmd];

			if( !Plugin.type ){
				Plugin.type = 'general';
			}

			// 将Selection模块赋值给自定义插件的的Selection属性，
			// 通过该Selection模块插件可以方便获取当前的选择内容
			// Selection.get('text|html|cursorElement');
			if( Plugin.type === 'base' ){
				log('run', '基础插件'+cmd+'添加成功');
			}else{
				Plugin.Selection = E.mod("Selection");
				log('run', '自定义插件'+cmd+'添加成功');
			}

			//如果添加到是方法插件，则后面的就不需要了
			if(typeof_obj === 'function'){
				return this;
			}

			//执行插件可能存在的初始化函数
			if(typeof Plugin.init == Consts.typeof_function){
				//Plugin.init( Plugin ); // 改为在ready时执行 2011-08-15

			}else{
				// 没有init 方法的插件，不需要显示面板
				Plugin.isNeedPanel = 0;
			}

			// 应用插件的lang、icon对象
			if( Plugin.lang && typeof Plugin.lang === "object" ){
				for( var langName in Plugin.lang ){
					E.lang(langName, Plugin.lang[langName]);
				}
			}
			if( Plugin.icon && typeof Plugin.icon === "object" ){
				for( var iconName in Plugin.icon ){
					if( typeof Plugin.icon[iconName] === 'object' ){
						E.icon(iconName, Plugin.icon[iconName]);
					}
				}
			}

			if( Plugin.style ){
				E.pluginStyle.push( Plugin.style );
			}

			//检查插件的依赖模块
			//对于核心模块，不需要检查，主要用来检查自定义模块
			if( Plugin.depends && $.isArray( Plugin.depends ) ){
				for(var j=0, len=Plugin.depends.length; j<len; j++){
					var name = Plugin.depends[j];
					//如果有依赖模块不存在，则将插件设置为禁用状态
					if( !E.mod(name) ){
						Plugin.disable();
						log('warning', '插件 ' + cmd +'缺少依赖模块: '+ name);
						break;
					}
				}
			}
		}

		return this;
	} else if(plugins[name]){
	// 获取插件
		var plugin = plugins[name];
		/*
		// 2011-05-03 22:03 潘雪鹏
		// 插件不论是否可用，都应该可以返回出去

		if( plugin.type == 'base' ){
			// 基础命令插件没有isEnabled方法
			return plugin;
		}else if( plugin.isEnabled() ){
			return plugin;
		}else{
			return null;
		}
		*/
		return plugin;
	}else{
		// 没有插件
		// log ...
		return null;
	}
});

/**
 * 记录按钮背景图片css坐标信息
 * @param string name
 * @param object obj
 * @return boolean|obj
 */
E.extend("icon", function E_icon( name, obj ){
	if(!name || typeof name != "string"){
		log('error', "E.icon(name, obj)方法的参数 name 必须是有效的字符串");
		return 0;
	}

	//转为局部变量
	var icons = E.icons;

	if( obj && Consts.re_object.test(typeof obj) ){
	//添加坐标信息
		if( icons[name] ){
		//已经存在，使用扩展方式进行合并
			icons[name]=$.extend(icons[name], obj);
		} else {
		//直接赋值
			icons[name] = obj;
			log('run', '按钮背景坐标'+name+'添加成功');
		}
		return 1;
	} else {
	//获取按钮背景图片坐标信息
		return icons[name] ? icons[name] : 0;
	}
});

/**
 * 语言信息
 * @param string name
 * @param object obj
 * @return boolean|obj
 */
E.extend("lang", function E_lang( name, obj ){
	if(!name || typeof name != "string"){
		log('error', "E.lang(name, obj)方法的参数 name 必须是有效的字符串");
		return 0;
	}

	//转为局部变量
	var langs = E.langs;

	if( obj && Consts.re_object.test(typeof obj) ){
	//添加语言信息
		if( langs[name] ){
		//已经存在，使用扩展方式进行合并
			langs[name]=$.extend(langs[name], obj);
		} else {
		//直接赋值
			langs[name] = obj;
			log('run', '语言包'+name+'添加成功');
		}
		return 1;
	} else {
	//获取语言信息
		return langs[name] ? langs[name] : 0;
	}
});


/**
 * 实现非IE浏览器执行bold命令时采用b标签，而不是使用span和css样式实现加粗
 * Before any other execCommands. Tested on ff3 ie7 ie8 safari chrome
 * URL: http://stackoverflow.com/questions/536132/stylewithcss-for-ie
*/
E.extend("_closeStyleWithCSS", function E_closeStyleWithCSS(dom){
	try {
		dom.execCommand("styleWithCSS", 0, false);
	}catch(e){
		try {
			dom.execCommand("useCSS", 0, true);
		}catch(e){
			try{
				dom.execCommand('styleWithCSS', false, false);
			}catch(e){}
		}
	}
});


/**
 * 将光标定位到编辑器的光标记忆处
 */
E.extend("focus", function E_focus(){
	var eid = E.eid, win = E.windows[eid];
	win && win.focus();
});

/**
 * 执行浏览器Dom命令，对可编辑区域的文字进行处理
 * 多被插件的 click 方法调用
 *
 */
E.extend("execCommand", function E_execCommand(cmd, bool, value){
	var dom = E.get('document');
	if( dom ){
		if (bool === null) {
			bool = false;
		}
		if(!Consts.IE && cmd=="BackColor"){
			cmd = "hiliteColor";
		}
		E.focus();
		dom.execCommand(cmd, bool, value);

		// 2011-07-22 潘雪鹏
		// 改为在action方法触发monitor，此处不再需要触发此事件
			/*
			if( !/^(redo|undo)$/i.test(cmd) ){
				E.monitor(cmd);
			}
			*/
		log('run', '执行编辑器命令：'+cmd);
	}else{
		// log
		log('error', '执行编辑器命令'+cmd+'时出现意外，没有得到dom对象');
	}
});

/**
 * 插入html代码到编辑器当中
 */
E.extend("pasteHTML", function E_pasteHTML( html ){
	var Selection = E.mod("Selection");
	Selection && Selection.set( html );

	// 注意，E.monitor()应该放在onpastehtml后面
	log('run', '向编辑器插入内容：'+html);

	// 2011-07-22 潘雪鹏
	// 有部分命令不会触发pasteHTML，如表格命令等，
	// 所以在此处触发monitor不合适，
	// 改为在action触发比较合适，
	// 无论是pasteHTML，还是仅执行命令都会触发monitor。
	/*
	E.monitor();

	// 当html代码包含img标签时，
	// 可能monitor不能准确调整编辑器的高度
	setTimeout(function(){
		E.monitor();
	}, 1000);
	*/
});

/**
 * 获取编辑器源代码
 */
E.extend("getHTML", function E_getHTML( isExecFilter ) {
	isExecFilter = (typeof isExecFilter === 'undefined') ? true: isExecFilter;
	var dom = E.get('document');
	return isExecFilter ? E.execFilter(dom, '', true) : dom.body.innerHTML;
});

/**
 * 设置编辑器源代码
 */
E.extend("setHTML", function E_setHTML( html ) {
	var dom = E.get('document');
		// 大字符串比较是否会存在性能问题？
	if( dom.body.innerHTML != html ){
		E.execFilter(dom, html);
		E.monitor("setHTML");

		// 如果存在表格插件则执行此操作
		// 重置内容和检查表格的id
		var T = E.plugin("InsertTable");
		if(T){
			T.setTableId();
		}


		log('run', '重置编辑器内容：'+dom.body.innerHTML);
	}
	return 1;
});

//内容过滤器基础对象，所有过滤器都会继承此对象的方法
var baseFilter = {
	type: 'dom',// dom | html

	dom: null,

	enabled: 1,

	check: function( ){
		if( this.isEnabled() ){
			return true;
		}else{
			return false;
		}
	},

	// 检查过滤器是否可用
	isEnabled: function( ){
		return this.enabled;
	},

	// 启用过滤器
	enable: function( ){
		this.enabled = 1;
	},

	// 禁用过滤器
	disable: function( msg ){
		this.enabled = 0;
	}
};

/**
 * 注册内容过滤器
 * 本来是想使用filter的，可以和plugin、mod等保持一致，
 * 但遗憾的是不能使用filter

 * 过滤器分为两种：1、使用节点遍历方式过滤，2、使用正则替换过滤
 */
E.extend("addFilter", function E_addFilter( filterName, Filter ) {
	if( typeof Filter === 'object'
		&& typeof Filter.replace === 'function'
	){
		log('run', '注册过滤器'+filterName);
		if( Filter.type ){
			if(Filter.type === 'html'){
				Filter = E.htmlFilters[ filterName ] = $.extend({}, baseFilter, Filter);
			}else if( Filter.type === 'out-html' ){
				Filter = E.htmlOutFilters[ filterName ] = $.extend({}, baseFilter, Filter);
			}else{
				// todo
				// log
			}
		}else{
			Filter = E.domFilters[ filterName ] = $.extend({}, baseFilter, Filter);
		}
	}else{
		E.alert('提示：内容过滤器'+ filterName +'缺少replace方法。');
	}
	return Filter;
});

/**
 * 执行内容过滤器
 */
E.extend("execFilter", function E_execFilter( dom, html, isGetHtml ) {
	var filter, domFilters = E.domFilters,
		htmlFilters = E.htmlFilters, innerHTML = '',
		option = E.get('option');

	if( html ){
		// 先执行html过滤器
		for( var filterName in htmlFilters ){
			filter = htmlFilters[filterName];
			if( $.inArray('filter.'+filterName, option.disabled) === -1
				&& filter.check()
			){
				filter.dom = dom;
				html = filter.replace( html );
				log('run', '执行html过滤器'+filterName);
			}
		}

		dom.body.innerHTML = html;
	}
	
	//执行 dom 过滤器
	for( var filterName in domFilters ){
		filter = domFilters[filterName];
		
		if( $.inArray('filter.'+filterName, option.disabled) === -1
				&& filter.check()
		){
			filter.dom = dom;
			filter.replace( dom );
			log('run', '执行dom过滤器'+filterName);
		}else{
			log('run', '禁用了dom过滤器'+filterName);
		}
	}

	innerHTML = dom.body.innerHTML;

	if( !html ){
		// 后执行html过滤器
		for( var filterName in htmlFilters ){
			filter = htmlFilters[filterName];
			if( $.inArray('filter.'+filterName, option.disabled) === -1
				&& filter.check()
			){
				filter.dom = dom;
				innerHTML = filter.replace( innerHTML );
				log('run', '执行html过滤器'+filterName);
			}
		}

		if(isGetHtml){
			// 后执行 html-out 过滤器
			for( var filterName in E.htmlOutFilters ){
				filter = E.htmlOutFilters[filterName];
				if( $.inArray('filter.'+filterName, option.disabled) === -1
					&& filter.check()
				){
					filter.dom = dom;
					innerHTML = filter.replace( innerHTML );
					log('run', '执行html-out过滤器'+filterName);
				}
			}
		}
	}

	//过滤头尾的空格和空p标签
	innerHTML = innerHTML.replace(/^(\s|&nbsp;|<br\s?\/?>)+/i, '')
		.replace(/(\s|&nbsp;|<br\s?\/?>)+$/i, '')
		.replace(/^(?:<p>(?:\s|<br\s?\/?>|&nbsp;)*<\/p>)+/i, '')
		.replace(/(?:<p>(?:\s|<br\s?\/?>|&nbsp;)*<\/p>)+$/i, '');

	return innerHTML;
});

E.extend("getButtonByName", function E_getButtonByName( name ){
	return $('#'+E.eid+'-'+name);
});

// 隐藏面板和菜单
E.extend("hidePanel", function E_hidePanel( from, type ){
	var Menu = E.mod('Menu'),
		Panel = E.mod('Panel'),
		Toolbar = E.mod('Toolbar');
	from = from || '';
	if(type === 'Panel'){
		if( Panel && $(".jqe-panel:visible").size() ){
			Panel.hide();
			E.log("run", "隐藏面板("+from+")");
		}
	}else if(type === 'Menu'){
		if( Menu && $(".jqe-menu:visible").size() ){
			Menu.hide();
			E.log("run", "隐藏菜单("+from+")");
		}
	}else{
		if( Panel && $(".jqe-panel:visible").size() ){
			Panel.hide();
			E.log("run", "隐藏面板("+from+")");
		}
		if( Menu && $(".jqe-menu:visible").size() ){
			Menu.hide();
			E.log("run", "隐藏菜单("+from+")");
		}
	}

	Toolbar && Toolbar.resetClickButton();
});

/**
  * 按钮点击触发方法，此方法调用按钮对应插件的 click 方法
  * @param target jQuery对象
  * 	点击工具栏的事件来源按钮对象
*/
E.extend("action", function E_action( target, e ){
	var name, eid,
		Plugin = null,
		bool = false,
		Panel = E.mod("Panel"),
		Menu = E.mod("Menu"),
		actionType = '';

	if( typeof target === 'string' ){
		// 左右键菜单点击过来的事件
		name = target;
		eid = E.eid;
		actionType = 'click';
		log('run', '编辑器'+eid+'即将执行菜单命令'+name);
	} else {
		name = target.closest('[name]').attr('name');
		eid = target.closest(".jqeditor").attr("id");
		// 得到当前编辑器的eid，同时赋值给jQEditor，确保其他操作可以得到正确E.eid
		E.setEid( eid );
		if ( target.is('.jqe-icon') ){
			// 点击的按钮图标
			actionType = 'click';
			log('run', '编辑器'+eid+'即将执行工具栏按命令'+name);
		}else if ( target.is('.jqe-button-menu') ){
			// 点击的右侧的下拉箭头
			actionType = 'menu';
			log('run', '编辑器'+eid+'即将执行工具栏下拉按钮命令'+name);
		}
	}

	Plugin = E.plugin(name);
	if( !Plugin ){
		log('error', '编辑器'+eid+'命令'+name+'不存在');
		return bool;
	}

	// 点击工具栏的事件来源
	if( typeof target === 'object' ){
		Plugin.target = target;
	}

	// 检查插件按钮是否处于可用状态
	if ( !Plugin.isEnabled() ){
		// 如果是禁用状态则终止并提示
		Plugin.target = null;
		if( Consts.IE ){
			E.showError( Plugin.title +'功能处于禁用状态' );
		}else{
			var option = E.get('option'), Lang = E.lang(option.lang);
			switch( name ){
				case 'Cut':
				case 'Paste':
				case 'Copy':
					E.showError(Lang[name+'DisabledMsg']);
					break;
				default:
					E.showError( Plugin.title +'功能处于禁用状态' );
			}
		}
		
		//log('run', '编辑器'+eid+"的"+Plugin.title +'功能处于禁用状态，执行失败');
		return bool;
	}

	Plugin.event = e;

	jQEditor.Plugin = E.Plugin = Plugin;

	// 2011.01.29 10.54 潘雪鹏
	// 点击工具栏按钮，由工具栏在创建时绑定的click事件调用E.action(target)
	if ( actionType == 'click' ){

		if(Panel && Plugin.isNeedPanel){
			bool = Plugin.click( Panel, Plugin, target );
		}else if( Menu ){
			if(e){target = $(e.target);}
			bool = Plugin.click( Menu, Plugin, target );
		}else{
			bool = Plugin.click( null, Plugin, target );
		}
		Plugin.event = null;

	}else if ( actionType == 'menu' ){
		var isVisible = false;
		// 判断点击的面板或菜单是否已经处于显示状态
		// 如果已经处于显示状态，则隐藏它
		if( Plugin.menuType === 'Panel' ){// Panel
			E.hidePanel('action', 'Menu');
			isVisible = Panel.isShow( Plugin );
		}else if( Plugin.menuType === 'Menu' ){ //Menu
			E.hidePanel('action', 'Panel');
			isVisible = Menu.isShow( Plugin );
		}

		if(isVisible){
			E.hidePanel('action');
		}else{
			bool = Plugin.menu( Panel, Plugin, target );
			log('run', '编辑器'+eid+'执行了工具栏下拉按钮命令'+name);
		}
	}else{
		// 记录错误
		// ...
	}

	E.Event.trigger( {type:'onafteraction', Plugin: Plugin} );

	// 2011-08-23 潘雪鹏
	// 撤销、重做命令和右侧的下拉箭头不需要触发onmonitor事件
	// 另外，点击后先弹出一个面板的插件也不需要触发onmonitor，
	//       而是在点击确定按钮时触发onmonitor
	// 点击下拉菜单也不需要触发onmonitor


	if( actionType === 'click'
		&& typeof Plugin['ok'] === 'undefined'
		&& typeof Plugin['get'] === 'undefined'
	){
		if( bool && !/(undo|redo)/i.test(name)){
			E.monitor(name);
		}

		// 执行完毕
		E.pluginExit();
	}
	return bool;
});

// 执行完毕
E.extend("pluginExit", function E_pluginExit( ){
	if( E.Plugin ){
		E.Plugin.target = null;
		jQEditor.Plugin = E.Plugin = null;
	}
});
/**
 * 创建一个可编辑的 iframe 区域
 * @return iframe.document
 */
E.extend("iframe", function E_iframe( option, container, content ){
	var config = E.config,
		eid = option.id,
		box = $('#'+eid),
		iframe = document.createElement("iframe"),
		html = '',
		dom, win;

	if( box.size() ){
		box.addClass("jqeditor");

		if( !container || typeof container !== 'object' ){
			container = box.find('.jqe-content');
		}
		if( !container.size() ){
			box.append('<div class="jqe-content"></div>');
			container = box.find(".jqe-content");
		}
	}else{
		alert('指定的编辑器id='+eid+'不存在。（E.iframe）');
	}

	if( !container || typeof container !== 'object' ){
		alert('没有指定iframe容器container。（E.iframe）');
		return false;
	}

	// 2011-08-25 潘雪鹏
	// frameBorder必须使用frameBorder而不能使用frameborder
	// 否则IE依旧会显示iframe的边框
	$(iframe).attr({frameBorder:0, name:'iframe_'+eid, tabIndex:'0'});
	if( option.iframeStyle ){
		$(iframe).css( option.iframeStyle );
	}

	if( option.autoHeight ){
		$(iframe).attr({scrolling: 'no'});
	}

	if( option.iframeSrc ){
		$(iframe).attr({src: option.iframeSrc});
	}

	// 注意！必须在对iframe进行操作之前将其添加到页面上去
	// 否则 iframe.contentDocument || iframe.contentWindow.document; 会出错
	// 这也是不能将iframe先返回再添加到页面上的原因

	$(container).empty().append(iframe).height(option.height);
	dom = iframe.contentWindow.document || iframe.contentDocument;
	dom.open();

	html = config.DOCTYPE+'<html xmlns="'+ config.xmlns +'" class="CSS1Compat" lang="zh-cn" dir="ltr" style="height:99%"><head>';

	// 2012-01-15 21:16 潘雪鹏
	// IE8+下，document编辑状态下（dom.body.contentEditable）
	// 有浮动的div会导致其下紧跟的p不明原因下移，非编辑状态没有这个问题，
	// 所以在找到更好的办法之前，暂时只能强制使用ie7(IE=EmulateIE7)核心。
	html+= '<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />';

	html+='<meta http-equiv="Content-Type" content="text/html; charset='+config.charset+'" />';

	if(jQEditor.styleContent.length){
		html+='<style>'+jQEditor.styleContent.join('\n')+'</style>\n';
	}else if (option.contentCssName){
		var cssId = 'jqe-style-content-'+option.contentCssName,
			cssVersion = option.contentCssVersion? option.contentCssVersion : datestamp;
			cssUrl = config.jqePath+'/skins/content_'+option.contentCssName+'.css?'+cssVersion;

		html+='<link id="'+cssId+'" href="'+cssUrl+'" type="text/css" rel="stylesheet" />';
	}

	// 自定义内容区域的样式
	if(typeof option.styleContent === 'string'){
		html+='<style>'+option.styleContent+'</style>\n';
	}else if( $.isArray(option.styleContent) ){
		html+='<style>'+option.styleContent.join('\n')+'</style>\n';
	}

	content = content || '<p>'+ config.text +'</p>';

	//html+='</head><body id="'+ eid +'">'+content+'</body></html>';
	// 给body添加height:99%样式，为了确保在编辑器区域点击可以使光标
	// 比如body区域内容很少时，但是编辑器区域高度是200px
	// 此时在，编辑器区域靠近下部的部分点击时，将不能时光标定位到编辑器当中
	// 通过添加height样式，即可解决此问题
	html+='</head><body id="'+ eid +'" style="height:99%;width:99.6%;">'+content+'</body></html>';
	dom.write(html);
	dom.close();
	if( option.contentEditable ){
		//将iframe设置为可编辑模式
		Consts.IE ? dom.body.contentEditable = 'true' : dom.designMode = 'On';
	}

	//存储相关对象，方便其他方法使用
	E.bodys[eid] = dom.body;
	E.iframes[eid] = iframe;
	E.windows[eid] = win = iframe.contentWindow;
	E.documents[eid] = dom;

	log('run', '构建编辑器'+eid+'的iframe编辑器区域');

	// 改为返回body后，在IE下会出现错误
	// 在create的setTimeout内
	return {dom:dom, win:win};
});

/**
 * 创建一个编辑区域
 * 构建工具栏
 */
E.extend("create", function E_create( option ){
	E.ready(); // 在创建编辑器之前要确保做好准备工作

	var eid;
	for(var i in E.defaults){
		if( option[i] == null ){
			option[i] = E.defaults[i];
		}
	}

	eid = option.id;

	if( E.options[ eid ] ){
		E.alert('编辑器id重复，请更换！');
		return;
	}

	E.options[ eid ] = option;
	// 注意，E.setEid(eid);必须在E.options[ eid ] = option;的下面
	E.setEid(eid);
	E.init( option );

	// 将内容复制到编辑器区域
	var html = $("#"+option.contentId).val();

	//构建编辑区域
	var o = E.iframe( option, null, html );
	if(o){
		// 执行过滤器
		E.execFilter(o.dom, html);

		//加载工具条
		//绑定事件
		E.Event.trigger( {type:'oncreate', value:option, html:html} );

		// 编辑器加载完成后的事件
		E.Event.trigger( {type:'oncomplete', value:option, html:html} );

		log('run', '编辑器'+eid+'创建完成');
	}
});

// 必须延迟执行
// E.ready() 在所有插件加载之后执行
setTimeout(function(){
	E.ready();
}, 30);

function loadStyle(cssPath){
	var head = document.getElementsByTagName("head"),
		link = document.createElement('link');

	link.setAttribute('type', 'text/css');
	link.setAttribute('rel', 'stylesheet');
	link.setAttribute('href', cssPath);
	if( head.length ){
		head[0].appendChild(link);
	}
}

/**
 * 编辑器准备工作，在编辑器js加载完成后执行，仅执行一次
 *
 */
E.extend('ready', function E_ready( ){
	if(E.Event){return}
	var plugins = E.plugins, Plugin;
	for(var name in plugins){
		Plugin = plugins[name];
		if( typeof Plugin.init === 'function' ){
			Plugin.init( Plugin );
		}
	}
	E.Event = E.mod('Event');

	//? ie下偶尔会出现E.Event找不到trigger方法
	try{
		E.Event.trigger( {type:'onready'} );
	}catch(e){
		log('warning', 'E.Event找不到trigger方法');
		setTimeout(function(){E.Event.trigger( {type:'onready'} )}, 1000);
	}
	// 加载样式
	// 之前一直是手工引用样式，
	// 为了和之前保持兼容先判断在没有引用的情况下再自动引入样式
	if( !$("link[href*=skin_base]").size() ){
		var path = jQEditor.config('jqePath');
		// ie下貌似不好使，暂时取消
		//$("head").append('<link rel="stylesheet" type="text/css" href="'+path+'/skins/skin_base.css" />');

		//loadStyle(path+'/skins/skin_base.css');
	}
});

/**
 * 编辑器初始化工作，在编辑器创建之前调用，每创建编辑器就会执行
 * 处理CSS
 */
E.extend("init", function E_init( option ){
	var skin = option.skin,
		icons = E.icon(skin),
		style=[],
		id='jqe-style-'+ skin +'-'+ option.id;

	if( icons ){
		for(var name in icons ){
			style.push('.jqe-'+skin+' .'+name+'{background-position:'+ icons[name].XY +';}');
			if( icons[name].XY == "none" ){
				style.push('#'+option.id+'-'+name+' .jqe-icon{background:none;}');
			}
		}
	}

	style.push(E.pluginStyle.join('\n'));

	// 2012-01-10 潘雪鹏
	// 防止一些页面针对标签设置优先级较高的样式，而使编辑器样式乱掉
	style.push("#"+option.id+" li.jqe-button{margin:4px 1px 2px;}");
	style.push("#"+option.id+" li.jqe-button span{margin:0;width:22px;}");
	style.push("#"+option.id+" li.jqe-button span.selected-color{width:18px;}");


	$("head").append('<style id="'+ id +'">'+ style.join('\n') +'</style>');

	log('run', '编辑器样式添加到head标签当中');
});

/**
 * 注册系统基础插件
 * 这些命令功能由浏览器默认实现，比如加粗、对齐等等
 *
 * 这些插件共用一个对象，在调用时修改此对象的name属性，
 * 在 E.action() 方法内完成对name属性的赋值。
 *
 * 除了这些基础插件，所有自定义插件不能使用共享对象，可以查看 E.plugin 方法，
 * 自定义插件继承自一个独立的实例化的对象。
E.extend("base_plugin", function E_base_plugin( names, obj ){
	var plugins = E.plugins;
	for(var i=names.length-1; i>=0; i--){
		cmd = $.trim(names[i]);
		plugins[cmd] = obj;
	}
});
*/

// 2011-05-08 潘雪鹏
// 改为统一使用plugin方法
E.plugin(Consts.baseCmds, {
	isNeedPanel:0,
	//type: base 说明是基础命令，如果plugin则说明是自定义插件
	type:'base',

	//点击插件按钮出发事件
	click:function( ){
		var cmd = this.name, dom = E.documents[E.eid];

		if( !Consts.IE && /^(Cut|Copy|Paste)$/i.test(cmd) ){
			var key='';
			switch(cmd){
				case 'Cut':key = 'Ctrl + X';break;
				case 'Copy':key = 'Ctrl + C';break;
				case 'Paste':key = 'Ctrl + V';break;
			}
			if (key){
				E.showError('提示：此功能仅在IE浏览器下可用，可以使用对应的快捷键:'+key);
			}
			return false;
		}

		if (!Consts.IE && Consts.closeStyleCmd.test(cmd) && E._closeStyleWithCSS){
		//如果不是ie浏览器则强制使用IE的格式，以保持格式统一
			E._closeStyleWithCSS(dom);
		}

		//range.select()方法必须在 execCommand 的前面，否则不起作用
		E.execCommand(cmd);

		return true;
	}
});

//最外层匿名函数结束
})( jQuery );